from tools import checker
from tools.convert import translate as t


class AutoProperty(object):
    def __init__(self, name, doc, field, default, check_type, check_values, readonly):
        checker.assertNotEmpty("name", name, prefix="属性")
        name = name.strip()
        self.readonly = bool(readonly)
        self.field = field if field != "_" else "_" + name
        self.default = default
        self.doc = doc
        self.name = name
        self.check_values = check_values
        if isinstance(check_type, type):
            self.check_type = check_type
        elif isinstance(check_type, tuple):
            self.check_type = tuple(filter(lambda x: isinstance(x, type), check_type))
        else:
            self.check_type = None

    def error(self, messgae):
        raise ValueError(t(messgae) % (t("Property"), self.name))

    @staticmethod
    def prop_proxy(instance, name, proxy=None):
        if instance is None:
            return None
        if not hasattr(instance, "__prop_proxy__"):
            instance.__prop_proxy__ = {}
        if proxy is None:
            return instance.__prop_proxy__.get(name)
        else:
            instance.__prop_proxy__[name] = proxy
            return proxy

    def get_default(self, instance):
        if instance is None or not hasattr(instance, "__prop_proxy__"):
            return self.default
        return None if instance.__prop_proxy__.get("have set:" + self.name) else self.default

    def clear_default(self, instance):
        if instance is None:
            return None
        if not hasattr(instance, "__prop_proxy__"):
            instance.__prop_proxy__ = {}
        instance.__prop_proxy__["have set:" + self.name] = 1

    def get(self, instance):
        # we get here when someone calls x.d, and d is a NonNegative instance
        # instance = x
        # owner = type(x)
        checker.assertNotNone("instance", instance)
        prop_proxy = AutoProperty.prop_proxy
        method = "_get_" + self.name
        handler = prop_proxy(instance, method)
        if handler is None:
            if not hasattr(instance, "_get_" + self.name):
                if self.field:
                    handler = prop_proxy(instance, method, lambda: instance.__dict__.get(self.field))
                else:
                    handler = prop_proxy(instance, method, lambda: self.error('无法获取 %s: "%s"'))
            else:
                handler = prop_proxy(instance, method, getattr(instance, "_get_" + self.name))
        if handler.__code__.co_argcount == 2:
            result = handler(self.doc)
        else:
            result = handler()
        return result if result is not None else self.get_default(instance)

    def set(self, instance, value):
        # we get here when someone calls x.d = val, and d is a NonNegative instance
        # instance = x
        # value = val
        checker.assertNotNone("instance", instance)

        if self.check_values:
            checker.assertRange(self.name, value, self.check_values, prefix="属性")
        elif self.check_type:
            checker.assertTypes(self.name, value, self.check_type, prefix="属性")

        self.clear_default(instance)
        prop_proxy = AutoProperty.prop_proxy
        method = "_set_" + self.name
        handler = prop_proxy(instance, method)
        if handler is None:
            if self.readonly:
                handler = prop_proxy(instance, method, lambda val: self.error('%s: `%s` 是只读的'))
            elif not hasattr(instance, method):
                if self.field:
                    handler = prop_proxy(instance, method, lambda val: instance.__dict__.__setitem__(self.field, val))
                else:
                    handler = prop_proxy(instance, method, lambda val: self.error('无法设置 %s: "%s"'))
            else:
                handler = prop_proxy(instance, method, getattr(instance, method))

        if self.field and instance.__dict__.get(self.field) == value:
            return

        if handler.__code__.co_argcount == 3:
            return handler(value, self.doc)
        return handler(value)

    def delete(self, instance):
        checker.assertNotNone("instance", instance)

        self.clear_default(instance)
        prop_proxy = AutoProperty.prop_proxy
        method = "_delete_" + self.name
        handler = prop_proxy(instance, method)

        if handler is None:
            if hasattr(instance, "_del_" + self.name):

                handler = prop_proxy(instance, method, getattr(instance, "_del_" + self.name))
            elif hasattr(instance, "_delete_" + self.name):
                handler = prop_proxy(instance, method, getattr(instance, "_delete_" + self.name))
            else:
                handler = prop_proxy(instance, method, lambda: self.error('无法删除 %s: "%s"'))

        if handler.__code__.co_argcount == 2:
            return handler(self.doc)
        return handler()


def prop(name, *, doc=None, bind_field=None, default=None, check_type=None, check_values=None, readonly=False):
    """
    自动完成属性, 自动触发 _set_{name}, _get_{name}, _del_{name} 方法回调
    :param name: 属性名称
    :param doc: 属性注释
    :param bind_field: 绑定字段
    :param default: 默认值
    :param check_type: 验证类型
    :param check_values: 验证类型
    :param readonly: 是否只读
    :return:
    """
    p = AutoProperty(name, doc, bind_field, default, check_type, check_values, readonly)
    return property(p.get, p.set, p.delete, doc)
